home *** CD-ROM | disk | FTP | other *** search
- /* functions for positioning and sizing windows
- 94/01/08 aih - added function to determine if a window can be dragged
- - moved WinCouldDrag from WindowZoomLib.c
- - moved several window positioning and sizing related
- functions from WindowLib.c
- 93/11/12 aih - split from WindowLib.c */
-
- #include <stdlib.h>
- #include <string.h>
- #include "DrawLib.h"
- #include "MathLib.h"
- #include "MemoryLib.h"
- #include "RectangleLib.h"
- #include "ScreenLib.h"
- #include "WindowLib.h"
- #include "WindoidWDEF.h"
-
- /* Stagger the window within the rectangle 'staggerRect'. The window's new
- rectangle is placed in the 'newRect' parameter. The window's minimum
- and maximum sizes are determined by 'sizeRect'. If all the slots in the
- stagger rectangle are full then 'staggerRect' is expanded to the default
- new rectangle for the window (ie, WinNewRect() is called). If the
- 'constantSize' parameter is true then the window's size is preserved
- while it is staggered, so that only its position changes; otherwise,
- the top left of the window is moved while its bottom right remains fixed.
- The 'newRect' parameter should initially contain the window's port
- rectangle (the rectangle specifies the width and height of the window
- when 'constantSize' is true).
-
- The running time of the algorithm is around O(Nslots * Nwindows^2),
- but it could be made O(n + n log n) if the windows were scanned
- and then sorted (n is number of windows), but it would take space
- proportional to n.
-
- Based on the Human Interface Notes, with ideas from code for
- Disinfectant v2.4 by John Norstad and from C/Shell by
- MacDTS distributed with the 7.0b4 Beta CD (February 1991). */
- static void WinStaggerInRect(WindowPtr window, Boolean constantSize,
- const Rect *sizeRect, const Rect *pstaggerRect, Rect *newRect)
- {
- WindowPtr check; /* window being checked */
- Rect staggerRect;/* rectangle to stagger in */
- Rect checkRect; /* content rectangle of window being checked */
- Point delta; /* distance between window being checked and slot */
- short occupied; /* number of windows occupying the slot being checked */
- short layer; /* number of windows which can be contained in a slot */
- Point margin; /* margin between each slot */
-
- require(WinValid(window));
- require(RectValid(pstaggerRect));
- require(RectValid(newRect));
-
- /* initialize margin */
- margin.h = 12;
- margin.v = 16;
-
- /* Look for layer with smallest number of windows in one of its slots.
- This loop must be executed at least once. The proof that this
- loop will terminate is that there are a finite number of
- visible windows (less than SHRT_MAX), and 'occupied' is bounded
- between 0 and the number of windows, whereas layer is bounded
- between 1 and SHRT_MAX. */
- staggerRect = *pstaggerRect;
- layer = 1;
- do {
-
- /* the stagger rectangle must be at least the minimum size */
- check(RectWidth(&staggerRect) >= sizeRect->left);
- check(RectHeight(&staggerRect) >= sizeRect->top);
-
- /* If maintaining a constant size for the window then start from the
- top left of the stagger rectangle. Otherwise, start with the largest
- possible rectangle, which is simply the entire stagger rectangle
- clipped to the size rectangle. */
- if (constantSize) {
- newRect->bottom = staggerRect.top + RectHeight(newRect);
- newRect->right = staggerRect.left + RectWidth(newRect);
- }
- else {
- newRect->bottom = staggerRect.top + min(sizeRect->bottom, RectHeight(newRect));
- newRect->right = staggerRect.left + min(sizeRect->right, RectWidth(newRect));
- }
- newRect->top = staggerRect.top;
- newRect->left = staggerRect.left;
-
- do {
-
- /* check all visible windows to see if they fall within the current
- slot */
- occupied = 0;
- check = WinFirstVisible(WinLayer(window));
- while (occupied < layer && check) {
-
- /* check if this slot is occupied */
- WinContentRect(check, &checkRect);
- delta.h = checkRect.left - newRect->left;
- delta.v = checkRect.top - newRect->top;
- if (abs(delta.h) < margin.h && abs(delta.v) < margin.v) {
-
- /* If the window that took our slot is closer to the lower-right
- corner than we are, then use this window's location as the
- basis for the slots from now on, provided that the new
- window's rectangle would not become too small. This will
- align new windows with previous windows that are not gridded
- to the default slot positions. The check for > 0 is necessary
- to prevent bouncing between two existing windows. This check
- guarantees that we are progressing with the evaluation. */
- occupied++;
- if (delta.h > 0 && delta.v > 0 &&
- (RectWidth(newRect) - delta.h >= sizeRect->left ||
- RectHeight(newRect) - delta.v >= sizeRect->top))
- {
- if (constantSize)
- OffsetRect(newRect, delta.h, delta.v);
- else {
- newRect->left += delta.h;
- newRect->top += delta.v;
- }
- }
- }
-
- /* check next visible window */
- check = WinNextVisible(check);
- }
-
- /* advance to next slot if current slot is occupied by too many
- windows */
- if (occupied >= layer) {
- if (constantSize)
- OffsetRect(newRect, margin.h, margin.v);
- else {
- newRect->left += margin.h;
- newRect->top += margin.v;
- }
- }
-
- /* repeat while the current slot is occupied and the new rectangle
- isn't too small and the new rectangle is contained within the
- stagger rectangle */
- } while (occupied >= layer &&
- RectWidth(newRect) > sizeRect->left &&
- RectHeight(newRect) > sizeRect->top &&
- RectWithin(newRect, &staggerRect));
-
- /* if all slots are occupied, and this is the first layer, then
- expand the stagger rectangle to the entire screen and try again
- (redoing the first layer) */
- if (occupied >= layer && layer == 1) {
- /* make sure the stagger rectangle isn't already the new window
- rectangle */
- WinNewRect(window, newRect);
- if (! EqualRect(&staggerRect, newRect)) {
- staggerRect = *newRect;
- layer = 0;
- }
- }
-
- } while (occupied >= layer++);
-
- /* adjust bottom left so rectangle doesn't exceed the maximum size */
- newRect->bottom = newRect->top + min(RectHeight(newRect), sizeRect->bottom);
- newRect->right = newRect->left + min(RectWidth(newRect), sizeRect->right);
-
- /* lots of assertions to ensure that this function does what it promises */
-
- /* ensure that if the window's size wasn't supposed to change that it
- really didn't */
- if (constantSize) {
- Rect portRect;
- WinPortRect(window, &portRect);
- ensure(RectWidth(&portRect) == RectWidth(newRect));
- ensure(RectHeight(&portRect) == RectHeight(newRect));
- }
-
- /* the new rectangle must be at least as large as the minimum size and
- no larger than the maximum size */
- ensure(sizeRect->left <= RectWidth(newRect) && RectWidth(newRect) <= sizeRect->right);
- ensure(sizeRect->top <= RectHeight(newRect) && RectHeight(newRect) <= sizeRect->bottom);
-
- /* ensure that the new rectangle is entirely contained within the stagger
- rectangle */
- ensure(RectWithin(&staggerRect, pstaggerRect));
- ensure(RectWithin(newRect, &staggerRect));
- }
-
- /* Stagger the window in the current screen, using width and height as the
- initial size for the window. See WinStaggerInRect for more details. */
- void WinStagger(WindowPtr window, Boolean constantSize,
- short width, short height)
- {
- Rect newRect;
- Rect sizeRect;
- Rect staggerRect;
- Rect oldSize, newSize;
-
- WinPortRect(window, &oldSize);
- WinSizeRect(window, &sizeRect);
- WinNewRect(window, &staggerRect);
- width = min(min(max(width, sizeRect.left), sizeRect.right), RectWidth(&staggerRect));
- height = min(min(max(height, sizeRect.top), sizeRect.bottom), RectHeight(&staggerRect));
- SetRect(&newRect, 0, 0, width, height);
- WinStaggerInRect(window, constantSize, &sizeRect, &staggerRect, &newRect);
- WinMove(window, newRect.left, newRect.top);
- WinSize(window, RectWidth(&newRect), RectHeight(&newRect));
- WinPortRect(window, &newSize);
- WinResize(window, RectWidth(&newSize) - RectWidth(&oldSize),
- RectHeight(&newSize) - RectHeight(&oldSize));
- WinZoomReset(window);
- WinZoomRemember(window);
- }
-
- /* position window in current screen */
- void WinPosition(WindowPtr window, short h, short v)
- {
- Rect structure, content, drag, new;
- Point pt;
-
- require(WinValid(window));
- WinRectangles(window, &structure, &content, &drag, &new);
- RectPositionInScreen(&structure, 2, 3, &pt);
- pt.h += content.left - structure.left;
- pt.v += content.top - structure.top;
- WinMove(window, pt.h, pt.v);
- WinZoomReset(window);
- WinZoomRemember(window);
- }
-
- /* True if the window could be dragged were its size and position to be set
- to the 'bounds' rectangle (given in global coordinates). */
- Boolean WinCouldDrag(WindowPtr window, const Rect *bounds)
- {
- RgnHandle dragRgn = NULL; /* window's drag region */
- RgnHandle grayRgn = NULL; /* copy of gray region */
- Rect strucRect; /* bounding box of window's structure region */
- Rect contRect; /* bounding box of window's content region */
- Rect dragRect; /* bounding box of window's drag region */
- Boolean result = false;
-
- require(WinValid(window));
-
- /* window must have a drag region */
- if (WinHasDrag(window)) {
-
- /* calculate drag region */
- /* program_note: this is a little messy and not the best place
- to do the calculation, but we can't just call
- WinDragRect since the window doesn't exist yet */
- dragRgn = BeginRgn();
- grayRgn = BeginRgn();
- dragRect = *bounds;
- if (WinIsFloat(window)) {
- if (WindoidDragTop(GetWVariant(window))) {
- dragRect.top -= kWindoidDragSize - 1;
- dragRect.bottom = bounds->top - 1;
- }
- else if (WindoidDragLeft(GetWVariant(window))) {
- dragRect.left -= kWindoidDragSize - 1;
- dragRect.right = bounds->left - 1;
- }
- else
- check(false);
- InsetRect(&dragRect, kWindoidBorderSize, kWindoidBorderSize);
- }
- else {
- dragRect.top -= 20;
- dragRect.bottom = bounds->top - 1;
- }
- check(RectValid(&dragRect));
- RectRgn(dragRgn, &dragRect);
-
- /* copy the gray region and inset the copy by SCREEN_MARGIN pixels to
- get the region within which the user can drag windows */
- CopyRgn(GetGrayRgn(), grayRgn);
- InsetRgn(grayRgn, SCREEN_MARGIN, SCREEN_MARGIN);
-
- /* the gray and drag regions need to intersect */
- SectRgn(grayRgn, dragRgn, dragRgn);
- result = ! EmptyRgn(dragRgn);
-
- EndRgn(dragRgn);
- EndRgn(grayRgn);
- }
- return(result);
- }
-
- /* true if the window can be dragged from its current position, which requires
- that the window is visible and that its drag region intersect the area within
- which the window could be dragged */
- Boolean WinCanDrag(WindowPtr window)
- {
- Rect bounds;
-
- WinContentRect(window, &bounds);
- return(WinVisible(window) && WinCouldDrag(window, &bounds));
- }
-
-
- /* This is the same as MoveWindow except it always passes false as the last
- parameter to MoveWindow so as not to disrupt window layers. */
- void WinMove(WindowPtr window, short left, short top)
- {
- require(WinValid(window));
- MoveWindow(window, left, top, false);
- ensure(WinValid(window));
- }
-
- /* Set the size of the window. The objects in the window are responsible
- for invalidating the areas they formerly occupied and the new areas
- they occupy. This function only invalidates the grow region, in
- addition to any new content region. */
- void WinSize(WindowPtr window, short width, short height)
- {
- GrafPtr port;
- Rect oldGrowRect;
- Rect newGrowRect;
-
- require(WinValid(window));
- GetPort(&port);
- SetPort(window);
- WinPortRect(window, &oldGrowRect);
- SizeWindow(window, width, height, true);
- WinPortRect(window, &newGrowRect);
- if (WinHasGrow(window)) {
- oldGrowRect.top = oldGrowRect.bottom - 15;
- oldGrowRect.left = oldGrowRect.right - 15;
- newGrowRect.top = newGrowRect.bottom - 15;
- newGrowRect.left = newGrowRect.left - 15;
- EraseRect(&oldGrowRect);
- InvalRect(&oldGrowRect);
- EraseRect(&newGrowRect);
- InvalRect(&newGrowRect);
- }
- SetPort(port);
- ensure(WinValid(window));
- }
-
- /* Drag the window. This replaces DragWindow since DragWindow selects the
- window, and selecting the window would disrupt window layers. */
- void WinDrag(WindowPtr window, Point where)
- {
- Point offset; /* amount to offset window */
- Rect stopRect; /* empty stop rectangle */
- Rect dragRect; /* rectangle enclosing window's drag region */
- Rect contentRect; /* window's content rectangle */
- Rect structureRect; /* window's structure rectangle */
- WindowPtr above = NULL; /* for calculating clipRgn */
- RgnHandle clipRgn = NULL;/* region to clip to */
-
- require(WinValid(window));
- require(WinVisible(window));
-
- /* calculate rectangles */
- SetRect(&stopRect, 0, 0, 0, 0);
- WinDragRect(window, &dragRect);
- WinContentRect(window, &contentRect);
- WinStructureRect(window, &structureRect);
-
- /* calculate clip region by removing the structure regions of
- all of the windows above this window */
- clipRgn = BeginRgn();
- CopyRgn(GetGrayRgn(), clipRgn);
- above = FrontWindow();
- while (above != window) {
- WindowPeek win = (WindowPeek) above;
- check(win != NULL);
- if (win->visible)
- DiffRgn(clipRgn, win->strucRgn, clipRgn);
- above = (WindowPtr) win->nextWindow;
- }
-
- /* drag outline */
- offset = DragRect(&structureRect, &dragRect, GetGrayRgn(), clipRgn, where);
-
- /* move window */
- if (offset.h || offset.v) {
- WinMove(window, contentRect.left + offset.h,
- contentRect.top + offset.v);
- }
- EndRgn(clipRgn);
- ensure(WinValid(window));
- }
-
- /* grow the window */
- void WinGrow(WindowPtr window, Point where)
- {
- long grow;
- Rect sizeRect;
-
- require(WinValid(window));
- WinSizeRect(window, &sizeRect);
- grow = GrowWindow(window, where, &sizeRect);
- if (grow)
- WinSize(window, (*(Point *) &grow).h, (*(Point *) &grow).v);
- ensure(WinValid(window));
- }
-